import npyscreen
import curses
import datetime
import Scheduler
import time
import logging
# Title: SchedulerCLI.py
# Author: Arnold Baghomian
# Project: Facultimes
# This is an Oregon State University built as a requirement for CS319. The
# project is a collaboration between Matthew Tanner, Arnold Baghomian, and
# Joseph Helgeson.

# this is the class that manages the forms - each form is one screen
class ScheduleApp(npyscreen.NPSAppManaged):
    # create the 3 forms and manage them
    def onStart(self):
        # effectively disable logging by setting level to CRITICAL
        logging.basicConfig(filename="scheduler.log",level=logging.CRITICAL)
        logging.info("starting log")

        # splash screen




        self.mf = self.addForm("MAIN", MainForm)

        self.sf = self.addForm("NAMES", SelectNamesForm)

        self.rf = self.addForm("RESULTS", ResultForm)

        # repeat so there is time to read this
        for i in range(3):
            npyscreen.notify_wait("Pick a set of faculty names and a meeting date-time window from the main screen. "+ \
            "The calculate button will take you to the results screen displaying both who can attend the whole time and what times "+
            "everyone can attend.", title = "Welcome to Facultimes", form_color = 'STANDOUT' ,   wrap = True, wide = True)

    # switch between the 3 forms by their name
    def changeForm(self, name):
        #logging.info("changeForm")
        # Switch forms.  NB. Do *not* call the .edit() method directly (which
        # would lead to a memory leak and ultimately a recursion error).
        # Instead, use the method .switchForm to change forms.
        self.switchForm(name)

        # By default the application keeps track of every form visited.
        # There's no harm in this, but we don't need it so:
        self.resetHistory()


        # def onInMainLoop(self):
            #self.mf.meetingDate1.display()
        #def onCleanExit(self):
            #npyscreen.notify_wait("Goodbye!")

# buttons to do various tasks
# this button applies the selected faculty names to the selected names list
# and returns to the MAIN form. It uses a global boolean to signal the names list should change
class ApplyButton(npyscreen.ButtonPress):
    def whenPressed(self):
        global canceled
        canceled = False
        #logging.warning("ApplyButton")
        self.parent.parentApp.changeForm("MAIN")
        #self.parent.editing = False

# this button returns to the MAIN form without changing the selected names list
# It uses a global boolean to signal the names list should NOT change
class CancelButton(npyscreen.ButtonPress):
    def whenPressed(self):
        global canceled
        #logging.warning("ApplyButton")
        canceled = True
        self.parent.parentApp.changeForm("MAIN")
        #self.parent.editing = False

# this button moves back to the MAIN form from the RESULTS form
# if the user wants to change some of the input parameters
class BackButton(npyscreen.ButtonPress):
    def whenPressed(self):
        logging.info("BackButton")
        self.parent.change_forms("MAIN")


# this button updates input parameters - the start/end times and the selected faculty name list -
# from the user interface objects. It is used in the MAIN form
class UpdateButton(npyscreen.ButtonPress):
    def whenPressed(self):
        logging.info("UpdateButton")
        self.parent.updateForm()
        self.parent.change_forms("RESULTS")

# this button exits the program. It is used in both the MAIN and RESULTS forms
class ExitButton(npyscreen.ButtonPress):
    def whenPressed(self):
        logging.info("ExitButton")
        self.parent.parentApp.setNextForm(None)
        self.parent.editing = False

# this button initiates moving to the NAMES form for picking names from the list
class PickNamesButton(npyscreen.ButtonPress):
    def whenPressed(self):
        logging.info("PickNamesButton")
        self.parent.parentApp.changeForm("NAMES")


# this interface class lets the user pick minutes a list
class MinutesComboBox(npyscreen.ComboBox):
    def display_value(self, vl):
        #vl = '{:02d}'.format(vl)
        if vl == '':
            return '00'
        else:
            svl = "%02d" % vl
            return svl

# this interface class lets the user pick hours from a list
class HoursComboBox(npyscreen.ComboBox):
    def display_value(self, vl):
        #vl = '{:02d}'.format(vl)
        if vl == '':
            return '00:'
        else:
            svl = "%02d:" % vl
            return svl

# this form is for picking out faculty names from a list
class SelectNamesForm(npyscreen.FormBaseNew):
    # set form parameters and create the user interface objects
    def create(self):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList, canceled
        self.cycle_widgets  = True
        canceled = False
        logging.info("SelectNamesForm")
        selectedFacultyList = []
        #test list was input like this
        #f = open("instructors.txt")
        #facList = f.readlines()
        #f.close()
        self.labelHelp = self.add(npyscreen.TitleFixedText, name = "Pick Faculty Names: select button with mouse, ^A applies, ^L cancels", relx = 1,  editable= False)
        self.labelFacultyList = self.add(npyscreen.TitleFixedText, name = 'Faculty Selected', relx = 0, rely = 3, editable= False)
        self.facultyList = self.add(npyscreen.MultiSelect, columns = facultyListWidth,
        values = facList)
        self.applyButton = self.add(ApplyButton, name="APPLY ^A", relx = 20, rely = 3)
        self.cancelButton = self.add(CancelButton, name="CANCEL ^L", relx = 30, rely = 3)
        self.add_handlers({"^L": self.cancelForm})
        self.add_handlers({"^A": self.applyForm})

    # used to perform a button function with a hotkey
    def cancelForm(self, *args, **keywords):
        self.cancelButton.whenPressed()

    # used to perform a button function with a hotkey
    def applyForm(self, *args, **keywords):
        self.applyButton.whenPressed()

    # this updates the gobal selected names list from the user interface object if APPLY was picked
    # and then it tells the form manager to go to the MAIN form
    def afterEditing(self):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList, canceled
        if not canceled:
            selectedFacultyList = self.facultyList.get_selected_objects()
        #self.color = "CRITICAL"
        self.parentApp.changeForm("MAIN")

    # this returns a list of the selected
    # not currently used
    def getList(self):
        return self.facultyList.get_selected_objects()


# this form is for getting user input of times and displaying selected names
class MainForm(npyscreen.FormBaseNew):
    # set form parameters and create the user interface objects
    def create(self):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList
        self.cycle_widgets  = True
        self.scheduler = Scheduler.Scheduler()
        logging.warning("MAainForm")

        # initalize the output list of names
        listAvailable = []
        LISTMINUTES = range(60)
        LISTHOURS = range(24)

        facList = self.scheduler.validUsers
        #test input data:
        #f = open("instructors.txt")
        #facList = f.readlines()
        #f.close()

        #listAvailable = facList[0:3]
        #OK_BUTTON_TEXT = "EXIT"
        facultyListWidth = 35
        facultyAvailableWidth = 35
        timeListWidth = 30

        self.columns = 132

        # title   help
        self.labelHelp = self.add(npyscreen.TitleFixedText, name = "select with mouse, ^K calculates, ^E exits",  rely = 0, editable= False)

        # input time area

        timeWindowYOffset = 3
        self.labelTimeWindows = self.add(npyscreen.TitleFixedText, name = 'Meeting Time Span', rely = timeWindowYOffset, editable= False)

        # get/show start time

        changeXEnd = 3+30+3+2+2 - 13
        hoursOffset = 18+9
        self.labelStartTime = self.add(npyscreen.TitleFixedText, name = "start",   editable = False, rely = timeWindowYOffset+1)
        self.meetingStartDate = self.add(npyscreen.DateCombo,allowPastDate = False, width  = 18, useDateTime = True, rely = timeWindowYOffset+1, relx = 9, columns = 18)
        current_time = datetime.datetime.now().time()

        # use today as default start and end dates unless it is after 4pm
        # in which case use tomorrow
        if current_time.hour < 16:
            self.meetingStartDate.value = self.meetingStartDate.date_or_datetime().today()
        else:
            self.meetingStartDate.value = self.meetingStartDate.date_or_datetime().today() + datetime.timedelta(24)
        self.hoursStart = self.add(HoursComboBox, relx = hoursOffset+1, rely = timeWindowYOffset+1, width = 3, values = LISTHOURS, value = 10)
        self.hoursStart.value = 9
        self.minutesStart = self.add(MinutesComboBox, relx = hoursOffset+4, rely = timeWindowYOffset+1, width = 2, values = LISTMINUTES)
        self.minutesStart.value = 0

        # get/show end time

        self.labelEndTime = self.add(npyscreen.TitleFixedText, name="end  ",   editable = False, rely = timeWindowYOffset+2)
        self.meetingEndDate = self.add(npyscreen.DateCombo, allowPastDate = False, width = 18, rely = timeWindowYOffset+2, columns = 18, relx = 9)
        self.meetingEndDate.value = self.meetingStartDate.value
        self.hoursEnd = self.add(HoursComboBox, rely = timeWindowYOffset+2, relx = hoursOffset+1, width = 3, values = LISTHOURS)
        self.hoursEnd.value = 17
        self.minutesEnd = self.add(MinutesComboBox, rely = timeWindowYOffset+2, relx = hoursOffset+4, width = 2, values = LISTMINUTES)
        self.minutesEnd.value = 0
        timeAvailableYOffset = 20

        # Select Faculty Names

        self.pickNamesButton=self.add(PickNamesButton, relx = 0, rely = 7,  name = 'Pick Faculty Names')
        self.labelFacultyList = self.add(npyscreen.TitleFixedText, name = 'Faculty Selected', rely = 9, editable= False)
        self.facultyListSelected = self.add(npyscreen.MultiSelect, columns = facultyListWidth, rely = 10,
        values=[])

        self.updateButton=self.add(UpdateButton, relx = 0, rely = 1, name = 'Calculate (^K)')
        self.exitButton=self.add(ExitButton, relx = 23, rely = 1,  name = 'Exit (^E)')


        self.add_handlers({"^E": self.exitForm})
        self.add_handlers({"^K": self.updateForm})

    # this sets the interface object's display of selected names to the global list
    def beforeEditing(self):
        self.facultyListSelected.values = selectedFacultyList

    # used to press a button by hotkey
    def exitForm(self, *args, **keywords):
        self.exitButton.whenPressed()

    # not used currently as there is no back button on this form
    def backForm(self, *args, **keywords):
        self.backButton.whenPressed()

    # this updates the input parameter values from the form's user interface objects
    def updateForm(self, *args, **keywords):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList, MAX_TIMES
        self.facultyListSelected.values = selectedFacultyList
        self.scheduler.users = selectedFacultyList
        sd = self.meetingStartDate.value
        sdt = datetime.datetime(sd.year,sd.month,sd.day,self.hoursStart.value, self.minutesStart.value)
        ed = self.meetingEndDate.value
        edt = datetime.datetime(sd.year,sd.month,sd.day,self.hoursEnd.value, self.minutesEnd.value)
        self.scheduler.startWindow = sdt
        self.scheduler.endWindow = edt
        self.scheduler.schedule()
        attendees = self.scheduler.attendees
        # for testing
        #attendees = []
        #for i in range(20):
        #    attendees.append("Harold Cartright Bloom"+str(i))
        self.parentApp.rf.facultyListAvailable.values = attendees
        # limit to number of time windows displayed
        gridDateTimes = self.scheduler.times
        gridTimes = []
        for w in gridDateTimes:
            gridTimes.append(w[0].strftime('%c') + ' - ' + w[1].strftime('%c'))

        #next 3 lines are to test the screen
        #gridTimes = []
        #for i in range(20):
        #    gridTimes.append("30 September, 2014 14:30 -  30 September, 2014 14 "+str(i))
        self.parentApp.rf.gridAvailable.values = gridTimes
        self.parentApp.changeForm("RESULTS")

    # this is used to tell the form manager to display another form by name
    def change_forms(self, name):
            self.parentApp.changeForm(name)

# this form displays the results of scheduling as a list of times and another list of names
class ResultForm(npyscreen.FormBaseNew):
    # set form parameters and create the user interface objects
    def create(self):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList, MAX_TIMES
        # max number of meeting times proposed (due to screen size limitations)
        MAX_TIMES = 7
        logging.warning("ResultForm")
        self.cycle_widgets  = True
        LISTMINUTES = range(60)
        LISTHOURS = range(24)


        # title   help
        self.labelHelp = self.add(npyscreen.TitleFixedText, name = "tab or select with mouse, ^B go back. ^E exit",   rely = 0, editable= False)

        self.backButton=self.add(BackButton, relx = 0, rely = 1, name = 'Back (^B)')
        self.exitButton2=self.add(ExitButton, relx = 20, rely = 1,  name = 'Exit (^E)')

        # times that work for all the selected names

        timeAvailableYOffsetp1 = 3
        self.labelWindowsAvailable = self.add(npyscreen.TitleFixedText, name = 'Times All Available',   editable= False, rely = timeAvailableYOffsetp1)
        self.gridAvailable = self.add(npyscreen.Pager, rely = timeAvailableYOffsetp1 + 1, max_height= MAX_TIMES, editable = True)
        # initialise the result of list times
        self.gridAvailable.values  = []
        # values used for testing purposes:
        #self.gridAvailable.values.append(['14 August, 2014 12:00    to','15 August, 2014 13:00'])
        #self.gridAvailable.values.append(['15 September, 2014 16:00 to','15 August, 2014 17:00'])


        # output lise of the faculty that are free for all requested times

        self.labelFacultyAvailable = self.add(npyscreen.TitleFixedText, name = 'Faculty Available',  rely = MAX_TIMES + 4, editable= False)
        self.facultyListAvailable = self.add(npyscreen.MultiLine, width = facultyAvailableWidth,  rely = MAX_TIMES + 5, editable=True,
        values = listAvailable )

        # attach handler methods to hotkeys
        self.add_handlers({"^E": self.exitForm})
        self.add_handlers({"^B": self.backForm})


    # set the MAIN form's name list user interface object to the global selected names list
    def beforeEditing(self):
        self.parentApp.mf.facultyListSelected.values = selectedFacultyList

    # handler for hotkey to exit form
    def exitForm(self, *args, **keywords):
        self.exitButton.whenPressed()

    # handler for hotkey to return to MAIN form
    def backForm(self, *args, **keywords):
        self.backButton.whenPressed()

    # this updates the result data
    # currently it is not used since the user gets to the RESULTS form
    # by updating the data on the MAIN form
    def updateForm(self, *args, **keywords):
        global facList,listAvailable, facultyAvailableWidth,facultyListWidth,timeListWidth, scheduler, selectedFacultyList, MAX_TIMES
        if selectedFacultyList:
            self.facultyListSelected.values = selectedFacultyList
            self.scheduler.users = selectedFacultyList
            sd = self.meetingStartDate.value
            sdt = datetime.datetime(sd.year,sd.month,sd.day,self.hoursStart.value, self.minutesStart.value)
            ed = self.meetingEndDate.value
            edt = datetime.datetime(sd.year,sd.month,sd.day,self.hoursEnd.value, self.minutesEnd.value)
            self.scheduler.startWindow = sdt
            self.scheduler.endWindow = edt
            self.scheduler.schedule()
            self.facultyListAvailable.values = self.scheduler.attendees
            gridTimes = self.scheduler.times
            # only the first 10 meeting times can be displayed
            if len(gridTimes) > MAX_TIMES:
                gridTimes = gridTimes[0:MAX_TIMES-1]
                gridTimes[MAX_TIMES-1] = 'More times not shown. Set smaller time window.'
            self.gridAvailable.values = gridTimes
            self.draw_form()
        else:
            self.facultyListSelected.values =[]
            self.facultyListAvailable.values = []
            self.gridAvailable.values = []
            self.draw_form()


    # this tells the form managing object display the MAIN form
    def change_forms(self, *args, **keywords):
            self.parentApp.changeForm("MAIN")



# this is the  main program
if __name__ == '__main__':
    TA = ScheduleApp()
    TA.run()